﻿#include "PageCache.h"

PageCache PageCache::_Instance;

// 获取⼀个K⻚的span
Span* PageCache::NewSpan(size_t k)
{
	assert(k > 0);
	// 大于128 page的直接向堆申请
	if (k > NPAGES - 1)
	{
		void* ptr = SystemAlloc(k);
		//Span* span = new Span;
		Span* span = _spanPool.New();

		span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->_n = k;

		_IdSpanMap[span->_pageId] = span;
		//_IdSpanMap.set(span->_pageId, span);

		return span;
	}
	//先检查第k个桶里有没有span
	if (!_spanLists[k].Empty())
	{
		Span* kSpan =  _spanLists[k].PopFront();
		for (PAGE_ID i = 0; i < kSpan->_n; i++)
		{
			_IdSpanMap[kSpan->_pageId + i] = kSpan;
		}
		return kSpan;
	}
	//检查一下后面的桶有没有Span，如果有可以把它进行切分
	for (size_t i = k + 1; i < NPAGES; i++)
	{
		if (!_spanLists[i].Empty())
		{
			Span* nSpan = _spanLists[i].PopFront();
			//Span* kSpan = new Span;
			Span* kSpan = _spanPool.New();

			//在nSpan的头部切一个k页的span下来
			//k页span返回
			//nspn 挂到对应映射的位置
			kSpan->_pageId = nSpan->_pageId;
			kSpan->_n = k;
			nSpan->_pageId += k;
			nSpan->_n -= k;
			_spanLists[nSpan->_n].PushFront(nSpan);
			//存储nspan 的首尾页号和nspan的映射，方便pagecache 回收内存时进行合并查找

			_IdSpanMap[nSpan->_pageId] = nSpan;
			_IdSpanMap[nSpan->_pageId + nSpan->_n - 1] = nSpan;
			for (PAGE_ID i = 0; i < kSpan->_n; i++)
			{
				_IdSpanMap[kSpan->_pageId + i] = kSpan;
			}
			return kSpan;
		}
	}
	//走到这里说明后面没有大页的span
	//向堆申请有一个NPAGES - 1页的span
	//Span* bigSpan = new Span;
	Span* bigSpan = _spanPool.New();

	void* ptr = SystemAlloc(NPAGES - 1);
	bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
	bigSpan->_n = NPAGES - 1;
	_spanLists[bigSpan->_n].PushFront(bigSpan);
	return NewSpan(k);
}

// 获取从对象到span的映射
Span* PageCache::MapObjectToSpan(void* obj)
{
	PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT;
	std::unique_lock<std::mutex> lock(_mutex);
	auto ret = _IdSpanMap.find(id);
	if (ret != _IdSpanMap.end())
	{
		return ret->second;
	}
	else
	{
		assert(false);
		return nullptr;
	}
	
}


// 释放空闲span回到Pagecache，并合并相邻的span
void PageCache::ReleaseSpanToPageCache(Span* span)
{
	// 大于128 page的直接还给堆
	if (span->_n > NPAGES - 1)
	{
		void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
		SystemFree(ptr);
		//delete span;
		_spanPool.Delete(span);

		return;
	}
	//向前合并span
	while (1)
	{
		PAGE_ID prevId = span->_pageId - 1;
		auto ret = _IdSpanMap.find(prevId);
		//前面的页号没有了，不合并
		if (ret == _IdSpanMap.end())
		{
			break;
		}
		Span* prevSpan = ret->second;

		// 前面相邻页的span在使用，不合并了
		if (prevSpan->_isUse == true)
		{
			break;
		}
		// 合并出超过128页的span没办法管理，不合并了
		if (prevSpan->_n + span->_n > NPAGES - 1)
		{
			break;
		}
		span->_n += prevSpan->_n;
		span->_pageId = prevSpan->_pageId;
		_spanLists[prevSpan->_n].Erase(prevSpan);

		//delete prevSpan;
		_spanPool.Delete(prevSpan);
	}

	//向后合并span
	while (1)
	{
		PAGE_ID nextId = span->_pageId + span->_n;
		auto ret = _IdSpanMap.find(nextId);
		if (ret == _IdSpanMap.end())
		{
			break;
		}
		Span* nextSpan = ret->second;
		if (nextSpan->_isUse == true)
		{
			break;
		}
		if (nextSpan->_n + span->_n > NPAGES - 1)
		{
			break;
		}
		span->_n += nextSpan->_n;
		_spanLists[nextSpan->_n].Erase(nextSpan);

		//delete nextSpan;
		_spanPool.Delete(nextSpan);
	}
	_spanLists[span->_n].PushFront(span);
	span->_isUse = false;
	_IdSpanMap[span->_pageId] = span;
	_IdSpanMap[span->_pageId + span->_n - 1] = span;
}
